
if (typeof Protoplasm == "undefined") {
    throw ("protoplasm.js not loaded, could not intitialize livegrid")
}
if (window.Control == undefined) {
    Control = {}
}
Protoplasm.loadStylesheet("livegrid.css", "livegrid");
Control.LiveGrid = Class.create({
    initialize: function(c, f, b, g, a){
        if (a == null) {
            a = {}
        }
        this.table = $(c);
        if (lg = this.table.retrieve("livegrid")) {
            throw ("This table is already a Live Grid")
        }
        this.fetchHandler = g;
        this.sortField = a.sortField ? a.sortField : null;
        this.sortDir = a.sortDir ? a.sortDir : null;
        var d = this.table.rows[0].cells.length;
        this.metaData = new Control.LiveGrid.MetaData(f, b, d, a);
        this.buffer = new Control.LiveGrid.Buffer(this.metaData);
        this.viewPort = new Control.LiveGrid.ViewPort(this.table, this.buffer, this.metaData);
		this.buffer.viewPort = this.viewPort;
        this.scroller = new Control.LiveGrid.Scroller(this.viewPort, this.metaData, this.requestContentRefresh.bind(this));
        if (a.selectable) {
            this.selector = new Control.LiveGrid.Selector(this.table, this.fetchHandler, this.metaData)
        }
        if (a.sortHeader) {
            this.sorter = new Control.LiveGrid.Sort(a.sortHeader, this.metaData, this.sort.bind(this))
        }
        if (a.captureKeyEvents) {
            this.captureKeys()
        }
        this.processingRequest = null;
        this.unprocessedRequest = null;
        if (a.prefetchBuffer || a.offset > 0) {
            var e = 0;
            if (a.offset) {
                e = a.offset;
                this.scroller.moveScroll(e);
                this.viewPort.scrollTo(this.scroller.rowToPixel(e))
            }
            this.requestContentRefresh(e)
        }
        this.table.store("livegrid", this)
    },
    resetContents: function(){
        this.scroller.unplug();
        this.scroller.moveScroll(0);
        this.scroller.plugin();
        this.buffer.clear();
        this.viewPort.clearContents()
    },
    captureKeys: function(){
        this.keyObserver = this.keyEvent.bindAsEventListener(this);
        Event.observe(window, "keypress", this.keyObserver)
    },
    releaseKeys: function(){
        Event.stopObserving(window, "keypress", this.keyObserver)
    },
    keyEvent: function(a){
        if (a.keyCode == Event.KEY_DOWN) {
            this.moveSelection(a, 1)
        }
        else {
            if (a.keyCode == Event.KEY_UP) {
                this.moveSelection(a, -1)
            }
            else {
                if (a.keyCode == Event.KEY_RETURN && a.shiftKey) {
                    this.selector.rowdblclickhandler()(a)
                }
                else {
                    if (a.keyCode == 33) {
                        this.moveSelection(a, -this.metaData.pageSize)
                    }
                    else {
                        if (a.keyCode == 34) {
                            this.moveSelection(a, this.metaData.pageSize)
                        }
                    }
                }
            }
        }
    },
    moveSelection: function(f, d){
        var g = this.metaData.totalRows - this.metaData.pageSize;
        if (g < 0) {
            g = 0
        }
        if (this.selector) {
            var b = (f.shiftKey && this.selector.previousSelections != null ? this.selector.lastRangeSelected : this.selector.lastRowSelected);
            if (b == null) {
                b = -1
            }
            if (b + d >= this.metaData.totalRows) {
                d = (this.metaData.totalRows - b) - 1
            }
            else {
                if (b + d < 0) {
                    d = -b
                }
            }
            var c = this.selector.currentOffset;
            var a = b + d;
            if (a - c >= this.metaData.getPageSize() || a - c < 0) {
                var h = c + d;
                if (h > g) {
                    h = g;
                    a = b + (g - h)
                }
                else {
                    if (h < 0) {
                        a = 0;
                        h = 0
                    }
                }
                this.scroller.moveScroll(h)
            }
            this.selector.rowmousedownhandler.bind(this.selector)(a - c)(f)
        }
        else {
            var c = parseInt(this.scroller.scrollerDiv.scrollTop / this.viewPort.rowHeight);
            var h = c + d;
            if (h > g) {
                h = g
            }
            this.scroller.moveScroll(h)
        }
    },
    sort: function(b, a){
        this.sortField = b;
        this.sortDir = a;
        this.refresh()
    },
    refresh: function(){
        this.resetContents();
        this.requestContentRefresh(0);
        if (this.selector) {
            this.selector.deselectAllRows()
        }
    },
    setTotalRows: function(a){
        this.metaData.setTotalRows(a);
        this.viewPort.setPageSize(this.metaData.getPageSize());
        this.scroller.updateSize();
        if (this.selector) {
            this.selector.applyRowBehavior()
        }
    },
    handleTimedOut: function(){
        this.processingRequest = null;
        this.processQueuedRequest()
    },
    fetchBuffer: function(c){
        if (this.buffer.isInRange(c) && !this.buffer.isNearingLimit(c)) {
            return
        }
        if (this.processingRequest) {
            this.unprocessedRequest = new Control.LiveGrid.Request(c);
            return
        }
        var b = this.buffer.getFetchOffset(c);
        this.processingRequest = new Control.LiveGrid.Request(c);
        this.processingRequest.bufferOffset = b;
        var a = this.buffer.getFetchSize(c);
        var d = false;
        this.fetchHandler(b, a, this.sortField, this.sortDir, this.updateData.bind(this));
        this.timeoutHandler = setTimeout(this.handleTimedOut.bind(this), 20000)
    },
    requestContentRefresh: function(a){
        this.fetchBuffer(a)
    },
    updateData: function(a){
        try {
            clearTimeout(this.timeoutHandler);
            this.buffer.update(a, this.processingRequest.bufferOffset);
            this.viewPort.bufferChanged()
        } 
        catch (b) {
        }
        finally {
            this.processingRequest = null
        }
        this.processQueuedRequest()
    },
    processQueuedRequest: function(){
        if (this.unprocessedRequest != null) {
            this.requestContentRefresh(this.unprocessedRequest.requestOffset);
            this.unprocessedRequest = null
        }
    }
});
Control.LiveGrid.staticFetchHandler = function(c, b){
    var a = Control.LiveGrid.convertToLiveGridRows(c, b);
    return function(k, d, f, e, l){
        if (f) {
            var h = -1;
            for (var g = 0; g < b.length; ++g) {
                if (b[g] == f) {
                    h = g;
                    break
                }
            }
            if (h > -1) {
                a = a.sort(function(m, i){
                    var n = m.columns[h];
                    var o = i.columns[h];
                    if (n < o) {
                        return (e.toLowerCase() == "desc" ? 1 : -1)
                    }
                    else {
                        if (n == o) {
                            return 0
                        }
                        else {
                            if (n > o) {
                                return (e.toLowerCase() == "desc" ? -1 : 1)
                            }
                        }
                    }
                })
            }
        }
        if (k >= a.length) {
            k = a.length - 1
        }
        if (k + d > a.length) {
            d = a.length - k
        }
        l(a.slice(k, k + d))
    }
};
Control.LiveGrid.convertToLiveGridRows = function(d, c){
    var e = Array();
    if (d.length) {
        for (var b = 0; b < d.length; ++b) {
            e[b] = {
                id: d[b].id,
                columns: new Array()
            };
            for (var a = 0; a < c.length; ++a) {
                e[b].columns[a] = d[b][c[a]]
            }
        }
    }
    else {
        e[0] = {
            id: d.id,
            columns: new Array()
        };
        for (var a = 0; a < c.length; ++a) {
            e[0].columns[a] = d[c[a]]
        }
    }
    return e
};
Control.LiveGrid.MetaData = Class.create({
    initialize: function(a, c, d, b){
        this.pageSize = a;
        this.totalRows = c;
        this.setOptions(b);
        this.scrollArrowHeight = 16;
        this.columnCount = d
    },
    setOptions: function(a){
        this.options = Object.extend({
            largeBufferSize: 7,
            nearLimitFactor: 0.2
        }, a || {})
    },
    getPageSize: function(){
        return this.totalRows < this.pageSize ? this.totalRows : this.pageSize
    },
    getTotalRows: function(){
        return this.totalRows
    },
    setTotalRows: function(a){
        this.totalRows = a
    },
    getLargeBufferSize: function(){
        return parseInt(this.options.largeBufferSize * this.pageSize)
    },
    getLimitTolerance: function(){
        return parseInt(this.getLargeBufferSize() * this.options.nearLimitFactor)
    }
});
Control.LiveGrid.Scroller = Class.create({
    initialize: function(b, a, c){
        this.metaData = a;
        this.viewPort = b;
        this.scrollHandler = c;
        this.isIE = navigator.userAgent.toLowerCase().indexOf("msie") >= 0;
        this.createScrollBar();
        this.scrollTimeout = null;
        this.rows = new Array()
    },
    isUnPlugged: function(){
        return this.scrollerDiv.onscroll == null
    },
    plugin: function(){
        this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this)
    },
    unplug: function(){
        this.scrollerDiv.onscroll = null
    },
    createScrollBar: function(){
        var a = this.viewPort.visibleHeight();
        this.scrollerDiv = document.createElement("div");
        var b = this.scrollerDiv.style;
        b.position = "relative";
        b.left = this.isIE ? "-6px" : "-3px";
        b.width = "19px";
        b.height = a + "px";
        b.overflow = "auto";
        this.heightDiv = document.createElement("div");
        this.heightDiv.style.width = "1px";
        this.heightDiv.style.height = parseInt(a * this.metaData.getTotalRows() / this.metaData.getPageSize()) + "px";
        this.scrollerDiv.appendChild(this.heightDiv);
        var c = this.viewPort.table;
        c.parentNode.parentNode.insertBefore(this.scrollerDiv, c.parentNode.nextSibling);
        setTimeout(this.plugin.bind(this))
    },
    updateSize: function(){
        var b = this.viewPort.table;
        var a = this.viewPort.visibleHeight();
        this.scrollerDiv.style.height = a + "px";
        if (this.metaData.getPageSize() == 0) {
            this.heightDiv.style.height = 0
        }
        else {
            this.heightDiv.style.height = parseInt(a * this.metaData.getTotalRows() / this.metaData.getPageSize()) + "px"
        }
    },
    rowToPixel: function(a){
        return (a / this.metaData.getTotalRows()) * this.heightDiv.offsetHeight
    },
    moveScroll: function(a){
        var b = this.metaData.totalRows == 0 ? 0 : a;
        if (this.metaData.options.onbeforescroll) {
            this.metaData.options.onbeforescroll(a, this.metaData.getPageSize(), this.metaData.totalRows)
        }
        this.scrollerDiv.scrollTop = this.rowToPixel(a);
        if (this.metaData.options.onscroll) {
            this.metaData.options.onscroll(a, this.metaData.getPageSize(), this.metaData.totalRows)
        }
    },
    handleScroll: function(){
        if (this.scrollTimeout) {
            clearTimeout(this.scrollTimeout)
        }
        var a = parseInt(this.scrollerDiv.scrollTop / this.viewPort.rowHeight);
        if (this.metaData.options.onbeforescroll) {
            this.metaData.options.onbeforescroll(a, this.metaData.getPageSize(), this.metaData.totalRows)
        }
        this.scrollHandler(a);
        this.viewPort.scrollTo(this.scrollerDiv.scrollTop);
        if (this.metaData.options.onscroll) {
            this.metaData.options.onscroll(a, this.metaData.getPageSize(), this.metaData.totalRows)
        }
        this.scrollTimeout = setTimeout(this.scrollIdle.bind(this), 1200)
    },
    scrollIdle: function(){
        if (this.metaData.options.onscrollidle) {
            this.metaData.options.onscrollidle()
        }
    }
});
Control.LiveGrid.Buffer = Class.create({
    initialize: function(a, b){
        this.startPos = 0;
        this.size = 0;
        this.metaData = a;
        this.rows = new Array();
        this.updateInProgress = false;
        this.viewPort = b;
        this.maxBufferSize = a.getLargeBufferSize() * 2;
        this.maxFetchSize = a.getLargeBufferSize();
        this.lastOffset = 0
    },
    getBlankRow: function(){
        if (!this.blankRow) {
            this.blankRow = new Array();
            for (var a = 0; a < this.metaData.columnCount; a++) {
                this.blankRow[a] = "&nbsp;"
            }
        }
        return this.blankRow
    },
    fixRows: function(a){
        for (var c = 0; c < a.length; ++c) {
            for (var b = 0; b < a[c].columns.length; ++b) {
                if (!a[c].columns[b]) {
                    a[c].columns[b] = "&nbsp;"
                }
            }
        }
        return a
    },
    update: function(a, d){
        var b = this.fixRows(a);
        if (this.rows.length == 0) {
            this.rows = b;
            this.size = this.rows.length;
            this.startPos = d;
            return
        }
        if (d > this.startPos) {
            if (this.startPos + this.rows.length < d) {
                this.rows = b;
                this.startPos = d
            }
            else {
                this.rows = this.rows.concat(b.slice(0, b.length));
                if (this.rows.length > this.maxBufferSize) {
                    var c = this.rows.length;
                    this.rows = this.rows.slice(this.rows.length - this.maxBufferSize, this.rows.length);
                    this.startPos = this.startPos + (c - this.rows.length)
                }
            }
        }
        else {
            if (d + b.length < this.startPos) {
                this.rows = b
            }
            else {
                this.rows = b.slice(0, this.startPos).concat(this.rows);
                if (this.rows.length > this.maxBufferSize) {
                    this.rows = this.rows.slice(0, this.maxBufferSize)
                }
            }
            this.startPos = d
        }
        this.size = this.rows.length
    },
    clear: function(){
        this.rows = new Array();
        this.startPos = 0;
        this.size = 0
    },
    isOverlapping: function(b, a){
        return ((b < this.endPos()) && (this.startPos < b + a)) || (this.endPos() == 0)
    },
    isInRange: function(a){
        return (a >= this.startPos) && (a + this.metaData.getPageSize() <= this.endPos())
    },
    isNearingTopLimit: function(a){
        return a - this.startPos < this.metaData.getLimitTolerance()
    },
    endPos: function(){
        return this.startPos + this.rows.length
    },
    isNearingBottomLimit: function(a){
        return this.endPos() - (a + this.metaData.getPageSize()) < this.metaData.getLimitTolerance()
    },
    isAtTop: function(){
        return this.startPos == 0
    },
    isAtBottom: function(){
        return this.endPos() == this.metaData.getTotalRows()
    },
    isNearingLimit: function(a){
        return (!this.isAtTop() && this.isNearingTopLimit(a)) || (!this.isAtBottom() && this.isNearingBottomLimit(a))
    },
    getFetchSize: function(c){
        var a = this.getFetchOffset(c);
        var b = 0;
        if (a >= this.startPos) {
            var d = this.maxFetchSize + a;
            if (d > this.metaData.totalRows) {
                d = this.metaData.totalRows
            }
            b = d - a
        }
        else {
            var b = this.startPos - a;
            if (b > this.maxFetchSize) {
                b = this.maxFetchSize
            }
        }
        return b
    },
    getFetchOffset: function(b){
        var a = b;
        if (b > this.startPos) {
            a = (b > this.endPos()) ? b : this.endPos()
        }
        else {
            if (b + this.maxFetchSize >= this.startPos) {
                var a = this.startPos - this.maxFetchSize;
                if (a < 0) {
                    a = 0
                }
            }
        }
        this.lastOffset = a;
        return a
    },
    getRows: function(g, e){
        var f = g - this.startPos;
        var b = f + e;
        if (b > this.size) {
            b = this.size
        }
        var d = new Array();
        var a = 0;
        for (var c = f; c < b; c++) {
            d[a++] = this.rows[c]
        }
        return d
    },
    convertSpaces: function(a){
        return a.split(" ").join("&nbsp;")
    }
});
Control.LiveGrid.ViewPort = Class.create({
    initialize: function(c, b, a){
        this.metaData = a;
        this.table = c;
        this.buffer = b;
        this.setPageSize(a.getPageSize());
        this.rowTemplate = null;
        this.lastDisplayedStartPos = 0;
        this.lastPixelOffset = 0;
        this.startPos = 0
    },
    setPageSize: function(a){
        this.fillTableRows(this.table, a);
        this.div = this.table.parentNode;
        this.rowHeight = a > 0 ? this.table.offsetHeight / a : 1;
        this.div.style.height = this.table.offsetHeight + "px";
        this.div.style.overflow = "hidden";
        this.visibleRows = a > 0 ? a + 1 : 0;
        this.fillTableRows(this.table, this.visibleRows);
        this.setOddOrEven(this.table, 0)
    },
    fillTableRows: function(d, e){
        if (d.rows.length || this.rowTemplate) {
            if (!this.rowTemplate) {
                this.rowTemplate = d.rows[0];
                for (var c = 0; c < this.rowTemplate.cells.length; ++c) {
                    if (!this.rowTemplate.cells[c].innerHTML) {
                        this.rowTemplate.cells[c].innerHTML = "&nbsp;"
                    }
                }
            }
            while (d.rows.length < e) {
                var b = d.insertRow(d.rows.length);
                b.className = this.rowTemplate.className;
                for (j = 0; j < this.rowTemplate.cells.length; ++j) {
                    var a = b.insertCell(b.cells.length);
                    if (this.rowTemplate.cells[j].className) {
                        a.className = this.rowTemplate.cells[j].className
                    }
                    if (this.rowTemplate.cells[j].width) {
                        a.width = this.rowTemplate.cells[j].width
                    }
                    if (this.rowTemplate.cells[j].style.width) {
                        a.style.width = this.rowTemplate.cells[j].style.width
                    }
                    a.innerHTML = "&nbsp;"
                }
            }
            while (d.rows.length > e) {
                d.deleteRow(d.rows.length - 1)
            }
        }
    },
    setOddOrEven: function(b, c){
        for (var a = 0; a < b.rows.length; ++a) {
            if (a % 2 == 1) {
                Element.removeClassName(b.rows[a], "even");
                Element.addClassName(b.rows[a], "odd")
            }
            else {
                Element.removeClassName(b.rows[a], "odd");
                Element.addClassName(b.rows[a], "even")
            }
        }
    },
    populateRow: function(e, d){
        var a = e.cells.length;
        var c = d.columns ? d.columns : d;
        if (d.id) {
            e.id = this.metaData.options.rowIdPrefix ? this.metaData.options.rowIdPrefix + d.id : d.id
        }
        for (var b = 0; b < a; b++) {
            e.cells[b].innerHTML = c[b]
        }
    },
    bufferChanged: function(){
        this.refreshContents(parseInt(this.lastPixelOffset / this.rowHeight))
    },
    clearRows: function(){
        if (!this.isBlank) {
            for (var a = 0; a < this.visibleRows; a++) {
                this.populateRow(this.table.rows[a], this.buffer.getBlankRow())
            }
            this.isBlank = true
        }
    },
    clearContents: function(){
        this.clearRows();
        this.scrollTo(0);
        this.startPos = 0;
        this.lastStartPos = -1
    },
    refreshContents: function(g){
        if (g == this.lastRowPos && !this.isPartialBlank && !this.isBlank) {
            return
        }
        if ((g + this.visibleRows < this.buffer.startPos) || (this.buffer.startPos + this.buffer.size < g) || (this.buffer.size == 0)) {
            this.clearRows();
            return
        }
        this.isBlank = false;
        var f = this.buffer.startPos > g;
        var b = f ? this.buffer.startPos : g;
        var a = (this.buffer.startPos + this.buffer.size < g + this.visibleRows) ? this.buffer.startPos + this.buffer.size : g + this.visibleRows;
        var k = a - b;
        var l = this.buffer.getRows(b, k);
        var d = this.visibleRows - k;
        var h = f ? 0 : k;
        var c = f ? d : 0;
        for (var e = 0; e < l.length; e++) {
            this.populateRow(this.table.rows[e + c], l[e])
        }
        for (var e = 0; e < d; e++) {
            this.populateRow(this.table.rows[e + h], this.buffer.getBlankRow())
        }
        this.isPartialBlank = d > 0;
        this.lastRowPos = g
    },
    scrollTo: function(a){
        if (this.lastPixelOffset == a) {
            return
        }
        this.refreshContents(parseInt(a / this.rowHeight));
        this.div.scrollTop = a % this.rowHeight;
        this.lastPixelOffset = a
    },
    visibleHeight: function(){
        return parseInt(this.div.style.height)
    }
});
Control.LiveGrid.Request = Class.create({
    initialize: function(b, a){
        this.requestOffset = b
    }
});
Control.LiveGrid.Selector = Class.create({
    initialize: function(b, c, a){
        this.table = b;
        this.fetchHandler = c;
        this.metaData = a;
        this.lastRowSelected = null;
        this.lastRangeSelected = null;
        this.rowIdPrefix = (a.options.rowIdPrefix ? a.options.rowIdPrefix : "");
        this.selectedClass = (a.options.selectedClass ? a.options.selectedClass : "selected");
        this.onrowselect = a.options.onrowselect;
        this.onrowopen = a.options.onrowopen;
        this.currentOffset = a.options.offset ? a.options.offset : 0;
        a.options.onbeforescroll = this.handleScroll.bind(this);
        this.selections = Array();
        this.previousSelections = Array();
        this.applyRowBehavior()
    },
    applyRowBehavior: function(){
        for (var a = 0; a < this.table.rows.length; ++a) {
            this.table.rows[a].onmousedown = this.rowmousedownhandler(a);
            this.table.rows[a].onmousemove = this.rowmousemovehandler(a);
            this.table.rows[a].onmouseup = this.rowmouseuphandler();
            this.table.rows[a].ondblclick = this.rowdblclickhandler();
            this.table.rows[a].onselectstart = function(){
                return false
            }
        }
    },
    rowmousedownhandler: function(a){
        return function(b){
            this.dragging = true;
            this.onrowclick(a, b);
            if (this.onrowselect) {
                this.onrowselect(b, this)
            }
            Event.stop(b);
            return false
        }
.bindAsEventListener(this)
    },
    rowmousemovehandler: function(a){
        return function(b){
            if (this.dragging) {
                this.onrowclick(a, b, true);
                if (this.onrowselect) {
                    this.onrowselect(b, this)
                }
            }
            return false
        }
.bindAsEventListener(this)
    },
    rowmouseuphandler: function(a){
        return function(b){
            this.dragging = false
        }
.bindAsEventListener(this)
    },
    rowdblclickhandler: function(){
        return function(a){
            if (this.onrowopen) {
                this.onrowopen(a, this)
            }
            Event.stop(a)
        }
.bindAsEventListener(this)
    },
    onrowclick: function(b, f, a){
        var d = this.table.rows[b].id;
        var g = this.currentOffset + b;
        if (f.shiftKey || a) {
            if (!this.previousSelections) {
                this.previousSelections = this.copyArray(this.selections)
            }
            else {
                this.selections = this.copyArray(this.previousSelections)
            }
            if (this.lastRowSelected < g) {
                for (var c = this.lastRowSelected; c <= g; ++c) {
                    this.selections[c] = this.table.rows[c - this.currentOffset].id
                }
            }
            else {
                if (this.lastRowSelected > g) {
                    for (var c = this.lastRowSelected; c >= g; --c) {
                        this.selections[c] = this.table.rows[c - this.currentOffset].id
                    }
                }
            }
            this.lastRangeSelected = g
        }
        else {
            if (f.ctrlKey) {
                this.selections[g] = this.selections[g] ? null : d;
                this.lastRowSelected = g;
                this.previousSelections = null
            }
            else {
                this.deselectAllRows(true);
                this.selections[g] = d;
                this.lastRowSelected = g;
                this.previousSelections = null
            }
        }
        this.redrawSelections()
    },
    copyArray: function(a){
        var b = new Array(a.length);
        for (var c = 0; c < a.length; ++c) {
            b[c] = a[c]
        }
        return b
    },
    selectedRows: function(){
        var b = new Array();
        var c = (this.rowIdPrefix ? this.rowIdPrefix.length : 0);
        for (var a = 0; a < this.selections.length; ++a) {
            if (this.selections[a]) {
                b[b.length] = this.selections[a].substring(c)
            }
        }
        return b
    },
    handleScroll: function(c, a, b){
        this.currentOffset = c;
        this.redrawSelections()
    },
    selectAllRows: function(){
        alert("Not implemented");
        if (this.onrowselect) {
            this.onrowselect(null, this)
        }
    },
    deselectAllRows: function(a){
        this.selections = new Array();
        this.redrawSelections();
        this.lastRowSelected = null;
        this.previousSelections = null;
        if (this.onrowselect) {
            this.onrowselect(null, this)
        }
    },
    redrawSelections: function(b){
        b = b ? b : this.currentOffset;
        for (var a = 0; a < this.table.rows.length; ++a) {
            if (b + a < this.metaData.totalRows) {
                if (this.selections[b + a]) {
                    Element.addClassName(this.table.rows[a], this.selectedClass)
                }
                else {
                    Element.removeClassName(this.table.rows[a], this.selectedClass)
                }
            }
        }
    }
});
Control.LiveGrid.Sort = Class.create({
    initialize: function(c, b, a){
        this.table = $(c);
        this.sortHandler = a;
        this.setOptions(b.options);
        this.columns = this.table.rows[0].cells;
        this.applySortBehavior(this.columns);
        this.initSortColumn()
    },
    initSortColumn: function(){
        var a = this.options.sortField;
        if (a) {
            var c = this.options.sortDirection ? this.options.sortDirection : "asc";
            for (var b = 0; b < this.columns.length; ++b) {
                if (this.columns[b].getAttribute("sortField") == a) {
                    this.setSortColumn(this.columns[b], c)
                }
            }
        }
    },
    setOptions: function(a){
        var b = Protoplasm.base("livegrid");
        var c = {
            sortAscendImg: b + "sort_asc.png",
            sortDescendImg: b + "sort_desc.png",
            imageWidth: 9,
            imageHeight: 5
        };
        this.options = Object.extend(c, a);
        new Image().src = this.options.sortAscendImg;
        new Image().src = this.options.sortDescendImg
    },
    applySortBehavior: function(a){
        for (var b = 0; b < a.length; ++b) {
            if (this.isSortable(b)) {
                var c = this.options.sortFields ? this.options.sortFields[b] : a[b].id;
                a[b].setAttribute("sortField", c);
                a[b].style.cursor = "pointer";
                a[b].onclick = this.onclick.bindAsEventListener(this);
                a[b].innerHTML += '<span id="' + c + '_img">&nbsp;&nbsp;&nbsp;</span>'
            }
        }
    },
    isSortable: function(a){
        return (!this.options.sortFields || this.options.sortFields[a])
    },
    onclick: function(b){
        var a = Event.element(b);
        while (a.tagName.toUpperCase() != "TD" && a.tagName.toUpperCase() != "TH") {
            a = a.parentNode
        }
        this.setSortColumn(a);
        this.executeSort()
    },
    setSortColumn: function(a, b){
        if (a.getAttribute("sortField") == this.sortField) {
            this.sortDirection = (this.sortDirection == "asc" ? "desc" : "asc")
        }
        else {
            this.sortField = a.getAttribute("sortField");
            this.sortDirection = "asc"
        }
        this.refreshColumnDisplay()
    },
    refreshColumnDisplay: function(){
        for (var a = 0; a < this.columns.length; ++a) {
            if (this.columns[a].onclick) {
                var b = $(this.columns[a].getAttribute("sortField") + "_img");
                if (this.columns[a].getAttribute("sortField") == this.sortField) {
                    if (this.sortDirection == "asc") {
                        b.innerHTML = '&nbsp;&nbsp;<img width="' + this.options.imageWidth + '" height="' + this.options.imageHeight + '" src="' + this.options.sortAscendImg + '"/>'
                    }
                    else {
                        if (this.sortDirection == "desc") {
                            b.innerHTML = '&nbsp;&nbsp;<img width="' + this.options.imageWidth + '" height="' + this.options.imageHeight + '" src="' + this.options.sortDescendImg + '"/>'
                        }
                        else {
                            b.innerHTML = "&nbsp;&nbsp;"
                        }
                    }
                }
                else {
                    b.innerHTML = "&nbsp;&nbsp;"
                }
            }
        }
    },
    executeSort: function(){
        this.sortHandler(this.sortField, this.sortDirection)
    }
});
Protoplasm.register("livegrid", Control.LiveGrid);
